package mgo

import (
	"go.mongodb.org/mongo-driver/bson"
)

// 使用mongo-driver操作mongodb需要用到该模块提供的bson。
// 主要用来写查询的筛选条件filter、构造文档记录以及接收查询解码的值，也就是在go与mongo之间做序列化。
// 其中，基本只会用到如下三种数据结构：
// bson.D{}: 对文档(Document)的有序描述，key-value以逗号分隔；
// bson.M{}: Map结构，key-value以冒号分隔，无序，使用最方便；
// bson.A{}: 数组结构，元素要求是有序的文档描述，也就是元素是bson.D{}类型。

// 过滤器
// 参考文档1：https://95eh.com/go_mongodb/#%E8%BF%87%E6%BB%A4%E5%99%A8
// 参考文档2：https://blog.csdn.net/tianwenxue/article/details/106316255

type FilterSymbol string

const (
	// 比较运算符
	Eq    FilterSymbol = "$eq"  // 匹配等于指定值，如：无序:bson.M{"name": bson.M{"$eq": "Jimmy"}} <=> 有序:bson.D{{"name", bson.D{{"$eq", "Jimmy"}}}}
	Gt    FilterSymbol = "$gt"  // 匹配大于指定值，如：bson.M{"age": bson.M{"$gt": 11}}
	Gte   FilterSymbol = "$gte" // 匹配大于或等于指定的值，如：bson.M{"age": bson.M{"$gte": 11}}
	Lt    FilterSymbol = "$lt"  // 匹配小于指定值，如：bson.M{"age": bson.M{"$lt": 11}}
	Lte   FilterSymbol = "$lte" // 匹配小于或等于指定值，如：bson.M{"age": bson.M{"$lte": 11}}
	Ne    FilterSymbol = "$ne"  // 匹配不等于指定值，如：bson.M{"name": bson.M{"$ne": "Jimmy Kuu"}}
	In    FilterSymbol = "$in"  // 匹配指定数组中的任意值，如：bson.M{"name": bson.M{"$in": []string{"Tom", "Jerry"}}}
	NotIn FilterSymbol = "$nin" // 匹配不在指定数组中的值，如：bson.M{"name": bson.M{"$nin": []string{"Tom", "Jerry"}}}
	// 指定字段全文搜索\模糊搜索
	Regex FilterSymbol = "$regex" // 使用正则表达式匹配文档，如：bson.M{"name": bson.M{"$regex": "^test[a-zA-Z]+"}}
	// 字段元素查询
	Exists FilterSymbol = "$exists" // 匹配具有指定字段的文档，如：bson.M{"name": bson.M{"$exists": true}}
	Type   FilterSymbol = "$type"   // 匹配字段为指定 bson 类型的文档，类型参考文档：https://www.runoob.com/mongodb/mongodb-operators-type.html
	// 数组元素查询
	All  FilterSymbol = "$all"  // 匹配指定元素包含指定数组中所有元素的文档，如：bson.M{"interests": bson.M{"$all": []string{"music", "reading"}}}
	Size FilterSymbol = "$size" // 匹配数组键值长度或数量等于指定值的文档，如：bson.M{"interests": bson.M{"$size": 3}}
	// 逻辑运算符
	And FilterSymbol = "$and" //匹配满足多个条件的文档，如：无序:bson.M{"age": 11, "sex":1} <=> 有序:bson.D{{"age",11}, {"sex",1}}
	Not FilterSymbol = "$not" //匹配不满足指定多个条件的文档，如：bson.M{"$not": bson.M{"age": 11, "sex":1}}
	Or  FilterSymbol = "$or"  //匹配满足多个任一条件的文档，如：bson.M{"$or": []bson.M{bson.M{"age": 11}, bson.M{"sex": 1}}}
	Nor FilterSymbol = "$nor" //匹配不满足指定多个任一条件的文档，如：bson.M{"$nor": []bson.M{bson.M{"age": 11}, bson.M{"sex": 1}}}
)

// Filter 逻辑运算符
// $and：匹配满足多个条件的文档
// $not：匹配不满足指定多个条件的文档
// $or"：匹配满足多个任一条件的文档
// $nor：匹配不满足指定多个任一条件的文档
type Filter struct {
	// 是否强制条件顺序
	Sort bool // 默认为 false-bson.M/true-bson.D
	// ID 条件
	Id interface{}
	// 等值条件信息
	Logical   FilterSymbol           // 逻辑运算符
	Where     map[string]interface{} // 多字段单条件等值匹配条件信息，条件无顺序
	SortWhere []*FilterUnit          // 多字段单条件等值匹配条件信息，条件有顺序
	// 其他匹配条件信息
	One *FilterUnit     // 单个字段复杂匹配条件：bson.M{"name": bson.M{"$ne": "Tom", "$exists": true}}
	And []*FilterUnit   // bson.M{"name": "Jimmy Kuu", "age": 33}、bson.M{"name": bson.M{"$eq", "Jimmy Kuu"}, "age": bson.M{"$eq", 33}}
	Not []*FilterUnit   // bson.M{"$not": bson.M{"name": "Jimmy Kuu", "age": 31}}
	Or  [][]*FilterUnit // bson.M{"$or": []bson.M{bson.M{"name": "Jimmy Kuu"}, bson.M{"age": 31}}}
	Nor [][]*FilterUnit // bson.M{"$nor": []bson.M{bson.M{"name": "Jimmy Kuu"}, bson.M{"age": 31}}}
}

type FilterUnit struct {
	Field string         // 条件对应的字段名
	Value interface{}    // 等于条件值,快捷字段
	Sort  bool           // 非等条件信息是否强制条件顺序
	Info  []*FilterValue // 非等条件信息
}

type FilterValue struct {
	Symbol FilterSymbol // 如果 symbol 为空则默认为 Eq
	Value  interface{}
}

func buildFilter(f *Filter) interface{} {
	// 1. 判断条件是否为空
	if f == nil {
		return bson.M{}
	}
	// 2. 组装 ID 条件
	if f.Id != nil {
		return bson.M{"_id": f.Id}
	}
	// 3. 判读是否要求顺序
	if f.Sort {
		return f.BuildFilterBsonD()
	}
	return f.BuildFilterBsonM()
}

func (f *Filter) BuildFilterBsonD() bson.D {
	// 1. 组装顺序等值匹配条件
	if len(f.SortWhere) > 0 {
		return buildBsonD(f.SortWhere)
	}
	// 2. 组装单字段复杂匹配条件
	if f.One != nil {
		return buildBsonD([]*FilterUnit{f.One})
	}
	// 3. 组装 And 多条件
	if len(f.And) > 0 {
		return buildBsonD(f.And)
	}
	// 4. 组装 Not 多条件
	if len(f.Not) > 0 {
		return bson.D{{"$not", buildBsonD(f.Not)}}
	}
	// 5. 组装 Or 多条件
	if len(f.Or) > 0 {
		d := make([]bson.D, 0)
		for _, v := range f.Or {
			d = append(d, buildBsonD(v))
		}
		return bson.D{{"$or", d}}
	}
	// 6. 组装 Nor 多条件
	if len(f.Nor) > 0 {
		d := make([]bson.D, 0)
		for _, v := range f.Nor {
			d = append(d, buildBsonD(v))
		}
		return bson.D{{"$nor", d}}
	}
	return nil
}

func buildBsonD(filter []*FilterUnit) bson.D {
	var box bson.D
	for _, v := range filter {
		// 判读是否存在快捷等值
		if v.Value != nil {
			box = append(box, bson.E{v.Field, v.Value})
			continue
		}
		// 获取条件值信息
		box = append(box, bson.E{v.Field, buildValue(v.Sort, v.Info)})
	}
	return box
}

func (f *Filter) BuildFilterBsonM() bson.M {
	// 1. 组装多等值条件
	if len(f.Where) > 0 {
		switch f.Logical {
		case Not:
			return bson.M{string(Not): f.Where}
		case Or:
			return bson.M{string(Or): f.Where}
		case Nor:
			return bson.M{string(Nor): f.Where}
		default:
			return f.Where
		}
	}
	// 2. 组装单字段复杂匹配条件
	if f.One != nil {
		return buildBsonM([]*FilterUnit{f.One})
	}
	// 3. 组装 And 多条件
	if len(f.And) > 0 {
		return buildBsonM(f.And)
	}
	// 4. 组装 Not 多条件
	if len(f.Not) > 0 {
		return bson.M{"$not": buildBsonM(f.Not)}
	}
	// 5. 组装 Or 多条件
	if len(f.Or) > 0 {
		m := make([]bson.M, 0)
		for _, v := range f.Or {
			m = append(m, buildBsonM(v))
		}
		return bson.M{"$or": m}
	}
	// 6. 组装 Nor 多条件
	if len(f.Nor) > 0 {
		m := make([]bson.M, 0)
		for _, v := range f.Nor {
			m = append(m, buildBsonM(v))
		}
		return bson.M{"$nor": m}
	}
	return nil
}

func buildBsonM(filter []*FilterUnit) bson.M {
	box := make(bson.M)
	for _, v := range filter {
		// 判读是否存在快捷等值
		if v.Value != nil {
			box[v.Field] = v.Value
			continue
		}
		// 获取条件值信息
		box[v.Field] = buildValue(v.Sort, v.Info)
	}
	return box
}

func buildValue(sort bool, value []*FilterValue) interface{} {
	// 存在顺序
	if sort {
		var cond bson.D
		for _, val := range value {
			if val.Symbol == "" {
				val.Symbol = Eq
			}
			cond = append(cond, bson.E{string(val.Symbol), val.Value})
		}
		return cond
	}
	// 不存在顺序
	cond := make(bson.M)
	for _, val := range value {
		if val.Symbol == "" {
			val.Symbol = Eq
		}
		cond[string(val.Symbol)] = val.Value
	}
	return cond
}

// 查询键值为null的字段: bson.M{"name": bson.M{"$in":[]interface{}{null}, "$exists": true}}
